package solver

import (
	"log"
)

func (g *Grid) skycraperLines(lines [9]House) (used bool) {
	for i := 0; i < 8; i++ {
		candCells, candCounts := sumupCandCells(&lines[i])
		for cand, count := range candCounts {
			if count != 2 {
				continue
			}
			cells := []*Cell{}
			for cellNum, candCell := range candCells[cand] {
				if candCell {
					cells = append(cells, lines[i].cells[cellNum])
				}
			}
			for j := i + 1; j < 9; j++ {
				yes, clearupCells := verifySkycraperLine(&lines[j], cand, cells)
				if !yes {
					continue
				}
				if g.clearupSkycraper(cand, clearupCells) {
					used = true
				}
			}
		}
	}
	return
}

func verifySkycraperLine(line *House, cand int, cells1 []*Cell) (yes bool, clearupCells []*Cell) {
	candCells, candCounts := sumupCandCells(line)
	if candCounts[cand] != 2 {
		return
	}

	cells2 := []*Cell{}
	for cellNum, candCell := range candCells[cand] {
		if candCell {
			cells2 = append(cells2, line.cells[cellNum])
		}
	}
	if cells1[0].rowNum == cells1[1].rowNum {
		if cells1[0].colNum == cells2[0].colNum {
			yes = true
			clearupCells = []*Cell{cells1[1], cells2[1]}
		}
		if cells1[1].colNum == cells2[1].colNum {
			yes = true
			clearupCells = []*Cell{cells1[0], cells2[0]}
		}
	} else { //cells1[0].colNum == cells1[1].colNum
		if cells1[0].rowNum == cells2[0].rowNum {
			yes = true
			clearupCells = []*Cell{cells1[1], cells2[1]}
		}
		if cells1[1].rowNum == cells2[1].rowNum {
			yes = true
			clearupCells = []*Cell{cells1[0], cells2[0]}
		}
	}

	return
}

func (g *Grid) clearupSkycraper(cand int, cells []*Cell) (used bool) {
	if len(cells) != 2 {
		return
	}
	relCells := make([][]*Cell, 2)
	for i, cell := range cells {
		relCells[i] = append(relCells[i], g.boxes[cell.boxNum].cells[:]...)
		relCells[i] = append(relCells[i], g.rows[cell.rowNum].cells[:]...)
		relCells[i] = append(relCells[i], g.cols[cell.colNum].cells[:]...)
	}

	sharedCells := shareCells(relCells[0], relCells[1])

	for _, cell := range sharedCells {
		if equalCells(cell, cells[0]) || equalCells(cell, cells[1]) {
			continue
		}
		//fmt.Println("relcell", cell.rowNum, cell.colNum)
		if cell.removeCandidate(cand + 1) {
			used = true
			//fmt.Println("rm cell", cell.rowNum, cell.colNum)
			log.Printf("(r%vc%v, r%vc%v) %v %v", cells[0].rowNum, cells[0].colNum, cells[1].rowNum, cells[1].colNum, cand+1, "Skycraper")
		}
	}

	return
}

func (g *Grid) Skycraper() (used bool) {
	if g.skycraperLines(g.rows) {
		used = true
	}
	if g.skycraperLines(g.cols) {
		used = true
	}
	return
}

type Chain struct {
	cand  int
	cells [2]*Cell
}

func findStrongChains(house *House) (chains []*Chain) {
	candCells, candCounts := sumupCandCells(house)
	for cand, count := range candCounts {
		if count != 2 {
			continue
		}
		chain := &Chain{}
		chain.cand = cand
		n := 0
		for cellNum, candCell := range candCells[cand] {
			if candCell {
				chain.cells[n] = house.cells[cellNum]
				n++
			}
		}
		chains = append(chains, chain)
	}
	return
}

func findStrongChainCell(cand int, baseCell *Cell, house *House) *Cell {
	var found *Cell
	foundCount := 0
	for _, cell := range house.cells {
		if cell.solved() || equalCells(baseCell, cell) {
			continue
		}
		if cell.candidates[cand] {
			found = cell
			foundCount++
		}
	}
	if foundCount != 1 {
		return nil
	}
	return found
}

func findWeakChainCells(cand int, baseCell *Cell, house *House) (weakCells []*Cell) {
	for _, cell := range house.cells {
		if cell.solved() || equalCells(baseCell, cell) {
			continue
		}
		if house.HouseType() == BoxHouse {
			if baseCell.rowNum == cell.rowNum || baseCell.colNum == cell.colNum {
				continue
			}
		} else {
			if baseCell.boxNum == cell.boxNum {
				continue
			}
		}

		if cell.candidates[cand] {
			weakCells = append(weakCells, cell)
		}
	}
	return
}

func (g *Grid) findStrongChainCellClearup(chain *Chain) (used bool) {
	for i, cell := range chain.cells {
		weakCells := findWeakChainCells(chain.cand, cell, &g.boxes[cell.boxNum])
		for _, weakCell := range weakCells {
			strongCell := findStrongChainCell(chain.cand, weakCell, &g.cols[weakCell.colNum])
			if strongCell == nil {
				continue
			}
			if g.clearupSkycraper(chain.cand, []*Cell{strongCell, chain.cells[1-i]}) {
				used = true
			}
		}
	}
	return
}

func (g *Grid) twoStringLines(rows [9]House) (used bool) {
	for i := 0; i < 9; i++ {
		chains := findStrongChains(&rows[i])
		for _, chain := range chains {
			if g.findStrongChainCellClearup(chain) {
				used = true
			}
		}
	}
	return
}

func (g *Grid) TwoStringKite() (used bool) {
	if g.twoStringLines(g.rows) {
		used = true
	}
	return
}

func (g *Grid) turbotFishClearup(chain *Chain) (used bool) {
	isChainRow := HouseType(chain.cells[0], chain.cells[1]) == RowHouse
	for i, cell := range chain.cells {
		var house *House
		if isChainRow {
			house = &g.cols[cell.colNum]
		} else {
			house = &g.rows[cell.rowNum]
		}
		weakCells := findWeakChainCells(chain.cand, cell, house)
		for _, weakCell := range weakCells {
			strongCell := findStrongChainCell(chain.cand, weakCell, &g.boxes[weakCell.boxNum])
			if strongCell == nil {
				continue
			}
			if g.clearupSkycraper(chain.cand, []*Cell{strongCell, chain.cells[1-i]}) {
				used = true
			}
		}
	}
	return
}

func (g *Grid) turbotFishLines(lines [9]House) (used bool) {
	for i := 0; i < 9; i++ {
		chains := findStrongChains(&lines[i])
		for _, chain := range chains {
			if g.turbotFishClearup(chain) {
				used = true
			}
		}
	}
	return
}

func (g *Grid) TurbotFish() (used bool) {
	if g.turbotFishLines(g.rows) {
		used = true
	}

	if g.turbotFishLines(g.cols) {
		used = true
	}
	return
}
